﻿using System.Windows;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System;
using System.Linq;
using System.Windows.Controls;
using System.Text.RegularExpressions;

namespace GMUtilitiesDotNet.AttachedProperties
{
    /// <summary>
    /// Class alters the behavior of TextBox based on attached property's instance.
    /// Attached property's type is IStringValidation, concrete instance defines how GenericTextBoxBehavior behaves.
    /// </summary>
    /// <example>
    /// <![CDATA[  
    ///     <Grid.Resources>
    ///         <utils:SomeTextBehavior x:Key="SomeTextBehavior" />
    ///     </Grid.Resources>
    ///     <TextBox Cinch:GenericTextBoxBehavior.TextBehavior="{StaticResource SomeTextBehavior}"/>  
    /// ]]>
    /// </example>
    public static class GenericTextBoxBehavior
    {
        #region TextBehavior DP
        public static readonly DependencyProperty TextBehaviorProperty =
            DependencyProperty.RegisterAttached("TextBehavior",
                typeof(IStringValidation), typeof(GenericTextBoxBehavior),
                    new UIPropertyMetadata(null, OnTextBehaviorChanged));

        /// <summary>
        /// Attached Property getter for the TextBehavior property.
        /// </summary>
        /// <param name="source">Dependency Object</param>
        /// <returns>Current property value</returns>
        public static IStringValidation GetTextBehavior(TextBox source)
        {
            return (IStringValidation)source.GetValue(TextBehaviorProperty);
        }

        /// <summary>
        /// Attached Property setter for the TextBehavior property.
        /// </summary>
        /// <param name="source">Dependency Object</param>
        /// <param name="value">Value to set on the object</param>
        public static void SetTextBehavior(TextBox source, IStringValidation value)
        {
            source.SetValue(TextBehaviorProperty, value);
        }

        /// <summary>
        /// This is the property changed handler for the TextBehavior property.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private static void OnTextBehaviorChanged(DependencyObject sender,
            DependencyPropertyChangedEventArgs e)
        {
            TextBox textBox = sender as TextBox;
            if (textBox == null)
            {
                string msg = String.Format("Attached property can be used only by {0} instances", typeof(TextBox));
                throw new ArgumentException(msg);
            }

            textBox.PreviewTextInput -= OnPreviewTextInput;
            DataObject.RemovePastingHandler(textBox, OnClipboardPaste);

            textBox.PreviewTextInput += OnPreviewTextInput;
            DataObject.AddPastingHandler(textBox, OnClipboardPaste);
        }

        #endregion

        /// <summary>
        /// This method handles paste and drag/drop events onto the TextBox.  It restricts the character
        /// set to numerics and ensures we have consistent behavior.
        /// </summary>
        /// <param name="sender">TextBox sender</param>
        /// <param name="e">EventArgs</param>
        private static void OnClipboardPaste(object sender, DataObjectPastingEventArgs e)
        {
            TextBox tb = (TextBox)sender;
            string text = e.SourceDataObject.GetData(e.FormatToApply) as string;

            if (!string.IsNullOrEmpty(text) && !Validate(tb, text))
            {
                e.CancelCommand();
            }
        }

        /// <summary>
        /// This checks if the resulting string will match the regex expression
        /// </summary>
        private static void OnPreviewTextInput(object sender, TextCompositionEventArgs e)
        {
            TextBox tb = (TextBox)sender;

            if (!Validate(tb, e.Text))
            {
                e.Handled = true;
            }
        }

        private static bool Validate(TextBox tb, string newContent)
        {
            string testString = string.Empty;
            // replace selection with new text.
            if (!string.IsNullOrEmpty(tb.SelectedText))
            {
                string pre = tb.Text.Substring(0, tb.SelectionStart);
                string after = tb.Text.Substring(tb.SelectionStart + tb.SelectionLength, tb.Text.Length - (tb.SelectionStart + tb.SelectionLength));
                testString = pre + newContent + after;
            }
            else
            {
                string pre = tb.Text.Substring(0, tb.CaretIndex);
                string after = tb.Text.Substring(tb.CaretIndex, tb.Text.Length - tb.CaretIndex);
                testString = pre + newContent + after;
            }

            // validate input string depending on text behavior instance
            bool isValid = false;

            IStringValidation textBehavior = GetTextBehavior(tb);
            if (textBehavior != null)
            {
                isValid = textBehavior.IsValid(testString);
            }

            return isValid;
        }
    }
}
